/*
 * =====================================================================================
 *
 *       Filename:  parameter_peu.c
 *
 *    Description:
 *					1、peu参数获取
 *					2、提供参数获取接口给程序调用
 *
 *        Version:  1.0
 *        Created:  2019年1月24日 16时34分45秒
 *       Revision:  none
 *
 *         Author:  lixinde (lxd), lixinde@phytium.com.cn
 *        Company:  Phytium Technology Co.,Ltd
 *        License:  GPL-2.0+
 *
 * =====================================================================================
 */
#include "parameter_peu.h"

static peu_config_t const peu_info = {
     .magic = PARAMETER_PCIE_MAGIC,
	 .version = CONFIG_PEU_PARAMETER_VERSION,
	 .size = CONFIG_PEU_PARAMETER_SIZE,
     .independent_tree = CONFIG_INDEPENDENT_TREE,
     .base_cfg = ((CONFIG_PCI_PEU1 | (CONFIG_PEU1_SPLIT_MODE << 1)) << PEU1_OFFSET | \
                 (CONFIG_PCI_PEU0 | (CONFIG_PEU0_SPLIT_MODE << 1))),
#if	CONFIG_PCI_PEU0
	 .ctr_cfg[0].base_config[0] = (CONFIG_PEU0_BUS_MODE << PEU_C_OFFSET_MODE) | (CONFIG_PEU0_C0_SPEED << PEU_C_OFFSET_SPEED),
	 .ctr_cfg[0].base_config[1] = (CONFIG_PEU0_BUS_MODE << PEU_C_OFFSET_MODE) | (CONFIG_PEU0_C1_SPEED << PEU_C_OFFSET_SPEED),
	 .ctr_cfg[0].base_config[2] = (CONFIG_PEU0_BUS_MODE << PEU_C_OFFSET_MODE) | (CONFIG_PEU0_C2_SPEED << PEU_C_OFFSET_SPEED),
	 .ctr_cfg[0].equalization[0] = CONFIG_PEU0_C0_EQ,
	 .ctr_cfg[0].equalization[1] = CONFIG_PEU0_C1_EQ,
	 .ctr_cfg[0].equalization[2] = CONFIG_PEU0_C2_EQ,
#endif
#if	CONFIG_PCI_PEU1
	 .ctr_cfg[1].base_config[0] = (CONFIG_PEU1_BUS_MODE << PEU_C_OFFSET_MODE) | (CONFIG_PEU1_C0_SPEED << PEU_C_OFFSET_SPEED),
	 .ctr_cfg[1].base_config[1] = (CONFIG_PEU1_BUS_MODE << PEU_C_OFFSET_MODE) | (CONFIG_PEU1_C1_SPEED << PEU_C_OFFSET_SPEED),
	 .ctr_cfg[1].base_config[2] = (CONFIG_PEU1_BUS_MODE << PEU_C_OFFSET_MODE) | (CONFIG_PEU1_C2_SPEED << PEU_C_OFFSET_SPEED),
	 .ctr_cfg[1].equalization[0] = CONFIG_PEU1_C0_EQ,
	 .ctr_cfg[1].equalization[1] = CONFIG_PEU1_C1_EQ,
	 .ctr_cfg[1].equalization[2] = CONFIG_PEU1_C2_EQ,
#endif
};

/*********************************/
int pcie_info_config(FILE *const fp, pm_info_t const *pm_d)
{
      if(NULL == fp){
         printf("fp is point to NULL\n");
         return -1;
     }

     fseek(fp, PM_PCIE_OFFSET, 0);
     fwrite(&peu_info, sizeof(peu_info), 1, fp);
     return 0;
}

static peu_config_t  peu_info_l;
static peu_config_t const * const peu_ptr = &peu_info_l;

/**********************************
*****与代码中解析参数函数一致*******
***********************************/

uint32_t pm_peu_version_config(void)
{
	return (peu_ptr->version);
}

/*
 * 	pcie 单根多根配置
 *	0:单根  0x1：多根
 */
uint32_t pm_peu_tree_config(void)
{
	return (peu_ptr->independent_tree);
}

/*
 * 	pcie 初始化配置
 *	0: 未初始化		0x1: 初始化
 */
uint32_t pm_peu_init_config(uint32_t peu)
{
	uint32_t ret;
	switch(peu)
	{
		case PEU0:
			ret = peu_ptr->base_cfg & 0x1;
			break;
		case PEU1:
			//PEU0未初始化则PEU1必定不初始化
			if(peu_ptr->base_cfg & 0x1)
				ret = (peu_ptr->base_cfg >> PEU1_OFFSET)& 0x1;
			else
				ret = 0;
			break;
		default:
			printf("Parametr : bed input 0x%x in func %s \n", peu, __func__);
			panic();
	}

	return ret;
}

/*
 * 	pcie 拆分模式配置
 *	0: x16		0x1: x8x8
 */
uint32_t pm_peu_split_config(uint32_t peu)
{
	uint32_t ret;
	switch(peu)
	{
		case PEU0:
			ret = (peu_ptr->base_cfg >> 1)& 0x1;
			break;
		case PEU1:
			ret = (peu_ptr->base_cfg >> (1+ PEU1_OFFSET)) & 0x1;
			break;
		default:
			printf("Parametr : bed input 0x%x in func %s \n", peu, __func__);
			panic();
	}

	return ret;
}

static void peu_check_input(uint32_t peu, uint32_t ctr,const char  *func)
{
	switch(peu)
	{
		case PEU0:
		case PEU1:
			break;
		default:
			printf("Parametr : bed input peu 0x%x in func %s \n", peu, func);
			panic();
	}
	switch(ctr)
	{
		case PEU_CTR_0:
		case PEU_CTR_1:
		case PEU_CTR_2:
		case 0xff:
			break;
		default:
			printf("Parametr : bed input ctr 0x%x in func %s \n", ctr, func);
			panic();
	}
}

uint32_t pm_get_ctr_mod(uint32_t peu)
{
	uint32_t ret = 0;

	peu_check_input(peu, 0xff, __func__);
	ret |= ((peu_ptr->ctr_cfg[peu].base_config[0] >> PEU_C_OFFSET_MODE) & 0x1) << 0;
	ret |= ((peu_ptr->ctr_cfg[peu].base_config[1] >> PEU_C_OFFSET_MODE) & 0x1) << 1;
	ret |= ((peu_ptr->ctr_cfg[peu].base_config[2] >> PEU_C_OFFSET_MODE) & 0x1) << 2;

	return ret;
}

//每两个bit代表一个控制器c0 = [0:1]  c1 = [2:3]  c2 = [4:5]
//gen auto = 0;gen1 = 1;gen2 = 2;gen3 = 3
uint32_t pm_get_peu_speed(uint32_t peu)
{
	uint32_t ret = 0;
	peu_check_input(peu, 0xff, __func__);

	ret |= ((peu_ptr->ctr_cfg[peu].base_config[0] >> PEU_C_OFFSET_SPEED) & 0x3) << 0;
	ret |= ((peu_ptr->ctr_cfg[peu].base_config[1] >> PEU_C_OFFSET_SPEED) & 0x3) << 2;
	ret |= ((peu_ptr->ctr_cfg[peu].base_config[2] >> PEU_C_OFFSET_SPEED) & 0x3) << 4;

	return ret;
}

uint32_t pm_get_peu_equalization(uint32_t peu, uint32_t ctr)
{
	uint32_t ret = 0;
	peu_check_input(peu, ctr, __func__);

	ret = peu_ptr->ctr_cfg[peu].equalization[ctr];

	return ret;
}

/**********************************
*****将参数表转化为可读数据********
***********************************/
void pcie_split_status(uint32_t peu)
{
	if(pm_peu_split_config(peu) == 0)
		printf("--X16X1");
	else
		printf("--X8X8X1");

}
//rc or ep mode
void pcie_ctr_mod_status(uint32_t peu)
{
	if(pm_get_ctr_mod(peu) == 0x7)
		printf("--RC");
	else
		printf("--EP");
}
//pcie speed
void pcie_ctr_speed_status(uint32_t peu)
{
	uint32_t tp, i, step;

	if(pm_peu_split_config(peu) == 0)
		step = 4;
	else
		step = 2;

	tp = pm_get_peu_speed(peu);
	printf("-");
	for(i = 0; i < 6; i += step){
		switch((tp >> i) & 0x3)
		{
		case GENa:
			printf("-Gena");
			break;
		case GEN1:
			printf("-Gen1");
			break;
		case GEN2:
			printf("-Gen2");
			break;
		case GEN3:
			printf("-Gen3");
			break;
		}
	}

}
void pcie_get_ctr_equalization(uint32_t peu)
{
	printf("<0x%x-0x%x-0x%x>", pm_get_peu_equalization(peu, PEU_CTR_0),				\
		pm_get_peu_equalization(peu, PEU_CTR_1), pm_get_peu_equalization(peu, PEU_CTR_2));
}

void pcie_init_status(uint32_t peu)
{
	if(pm_peu_init_config(peu)){
			printf("PEU %d : init", peu);
			pcie_split_status(peu);
			pcie_ctr_mod_status(peu);
			pcie_ctr_speed_status(peu);
			printf("\nPEU %d : equalization ", peu);
			pcie_get_ctr_equalization(peu);
	}else{
			printf("PEU %d : not init", peu);
	}
	printf("\n");
}

void prt_pcie_base_info(void)
{
	printf("=======================================\n");

	printf("PCIE info : \n");
	printf("version = 0x%x\n", pm_peu_version_config());
	printf("independent_tree mode: %s\n",IS_ENABLE(pm_peu_tree_config()));
	pcie_init_status(PEU0);
	pcie_init_status(PEU1);
	printf("=======================================\n");

}

int get_pcie_info(FILE *const fp, parameter_base_t const *p_base)
{
    if(NULL == fp){
		ERROR("fp is point NULL %s \n", __func__);
		return -1;
	}
	fseek(fp, p_base->addr + PM_PCIE_OFFSET, 0);
	fread(&peu_info_l,sizeof(peu_config_t),1,fp);

    if(PARAMETER_PCIE_MAGIC != peu_ptr->magic){
		ERROR("can not get right magic 0x%x, %s \n", peu_ptr->magic, __func__);
		return -1;
	}

	prt_pcie_base_info();
	return 0;
}

int check_pcie(FILE * const fp, uint64_t base)
{
	uint32_t magic;
	fseek(fp, base + PM_PCIE_OFFSET, 0);
	fread(&magic,sizeof(magic),1,fp);
	if(PARAMETER_PCIE_MAGIC != magic)
		return -1;
	else
		return 0;
}

pm_list_fun_t peu_fun = {
	.cfg_fnc = pcie_info_config,
	.ck_fnc = check_pcie,
	.prf_fnc = get_pcie_info,
};